function load_enemy_tables()

	-- enemy movement commands

	e_directions = {

		["up"] = function(self) self.y = self.y - 1 end,
		["down"] = function(self) self.y = self.y + 1 end,
		["right"] = function(self) self.x = self.x + 1 end,
		["left"] = function(self) self.x = self.x - 1 end,

	}



end



Enemy = class('Enemy')

function Enemy:initialize(name, x, y, image, grid, pathf, layout, patrolling, points, floor_type, health, ...)

	self.name = name or "default enemy name"
	self.x = x or 14
	self.y = y or 28
	self.health = health or 25
	self.image = image or herb_guy_img
	self.grid = grid or mGrid
	self.pathf = pathf or mFinder
	self.counter = 0
	self.step = 1
	self.rotation = 0
	self.scaleX = scale
	self.scaleY = scale
	self.lit = false
	self.can_move = true
	self.layout = layout
	self.patrol_p = points
	self.patrol_stage = 1
	self.patrol_t = patrolling or false
	self.cadence = 5
	self.speed = 0.75
	self.floor_type = floor_type or floor_tile



	if not self.grid then
		print("grid not defined")
	end

	if not self.pathf then
		print("pathf not defined")
	end

	-- enemies can be initiliazed with built in function executions, handled just below
	-- ROCK ON

	self.init = {...}

	for i = 1, #self.init do

		self.init[i](i)

	end
	--[[

	if self.grid and self.pathf then

		self:track_player()

	end

	]]

	-- modify enemy movement: initialize(), update() and move()
	-- uncomment the following in all to auto track player
	--self:track_player_t()
	local e = #enemies+1
	self.id = e
	enemies[e] = self
end

function Enemy:draw_all()

	-- draws our enemies, duh

	for i = 1, #enemies do
		--print("drawing ", enemies[i].name, " at ", enemies[i].x, enemies[i].y)
		if enemies[i] then
			if enemies[i].x and enemies[i].y then
				if (enemies[i].x <= map_x + range_x
					and enemies[i].x > map_x)
					and (enemies[i].y <= map_y + range_y
					and enemies[i].y > map_y) then

				enemies[i]:draw()

				end
			end

		end

	end

end

function Enemy:set_lit_all( bLit )
	if bLit ~= nil then
		for i = 1, #enemies do
			enemies[i].lit = bLit
		end
	else
		print("ENEMY set bLit error no bLit")
	end
end

function Enemy:assemble(lm)

	-- badass dynamic threading assembly
	-- takes self position, player position,
	-- computes shortest path in a thread


	local s = [[
		require'love.filesystem'
		print("checking j .. ", love.filesystem.exists('jumper'))
		Grid = require'jumper.grid'
		Pathfinder = require'jumper.pathfinder'
		require'src.util'

		local x = ]] .. self.x ..[[

		local y = ]] .. self.y .. [[

		local dx = ]] .. pX .. [[

		local dy = ]] .. pY .. [[

		local floor_tile = ]] .. self.floor_type .. [[

		local layout = ]] .. table.tostring(self.layout) .. [[

		print("x : ", x, " y : ", y ," dx : ", dx , " dy : ", dy , " floor_tile : ", floor_tile)
		--print(table.tostring(layout))

		local tt = love.thread.getChannel("path_progress")
		local tc = love.thread.getChannel("len")
		local tct = love.thread.getChannel("counted_path")
		local mGrid = Grid(layout)
		local mFinder = Pathfinder(mGrid, 'JPS', floor_tile)
		mFinder:setMode('ORTHOGONAL')

		print("thread ", tt:getName() ," started")

		local path = mFinder:getPath(x, y, dx, dy)

		new_path = {}
		step = 1

		if path then
			print("Path found! Length: ", path:getLength())
			path_len = path:getLength()
			local channel_count = {}
			for node, count in path:nodes() do

				--new_path[count] = node

				channel_count[#channel_count+1]= 'c'..count, count .. "-".. node.x .. "-" .. node.y

				--print("step :", count, "path x: ", node.x, " path y: ", node.y)
				step = step + 1



			end
			--print(table.tostring(path))
			tct:push(channel_count)
			tc:push(path_len)

		end


		tt:push('done')
		print("pathing thread done")

	]]

	self.tpr = love.filesystem.newFileData( s, "e_track_player.lua", "file")


	--print(s)

end

function Enemy:assemble_pos(x, y)


	-- dynamic threading assembly part 2
	-- this allows us to path from our self position
	-- to any x, y coord on the grid

	

	local s = [[
		require'love.filesystem'
		--print("checking j .. ", love.filesystem.exists('jumper'))
		Grid = require'jumper.grid'
		Pathfinder = require'jumper.pathfinder'
		require'src.util'

		local x = ]] .. self.x ..[[

		local y = ]] .. self.y .. [[

		local dx = ]] .. x .. [[

		local dy = ]] .. y .. [[

		local floor_tile = ]] .. self.floor_type .. [[

		local layout = ]] .. table.tostring(self.layout) .. [[

		--print("x : ", x, " y : ", y ," dx : ", dx , " dy : ", dy , " floor_tile : ", floor_tile)
		--print(table.tostring(layout))

		local tt = love.thread.getChannel("path_progress")
		local tc = love.thread.getChannel("len")
		local mGrid = Grid(layout)
		local mFinder = Pathfinder(mGrid, 'JPS', floor_tile)
		mFinder:setMode('ORTHOGONAL')

		--print("thread ", tt:getName() ," started")

		local path = mFinder:getPath(x, y, dx, dy)

		new_path = {}
		step = 1

		if path then
			--print("Path found! Length: ", path:getLength())
			path_len = path:getLength()
			local count_table = {}
			for node, count in path:nodes() do

				--new_path[count] = node
				count_table[#count_table+1] = count .. "-".. node.x .. "-" .. node.y
				

				--print("step :", count, "path x: ", node.x, " path y: ", node.y)
				step = step + 1



			end
			--print(table.tostring(path))
			local tct = love.thread.getChannel("counted_path")
			tct:push(count_table)
			tc:push(path_len)

		end


		tt:push('done')
		--print("pathing thread done")

	]]

	self.tpr = love.filesystem.newFileData( s, "e_goto_x_y.lua", "file")


	--print(s)


end

function Enemy:random_patrol( point_num )

	local pot_pois = {}
	local final_pois = {}
	local iter = 1
	self.total_random_patrol_points = point_num
	for y = 1, #self.layout do

		for x = 1, #self.layout[y] do

			if self.layout[y][x] == floor_tile then
				-- find all floor tiles in the map, add them to pot_pois
				pot_pois[iter] = {}

				pot_pois[iter].x = x
				pot_pois[iter].y = y

				iter = iter + 1

			end


		end

	end

	if #pot_pois < point_num then print("not enough floor spaces for requested amount of random points") return end

	for i = 1, point_num do
		-- randomly add certain floor tiles to be patrol nodes
		local r = RNG:random(#pot_pois)

		final_pois[i] = pot_pois[r]

	end

	-- set the patrol node
	self.patrol_p = final_pois


end

function Enemy:goto(x, y)

	-- big surprise, enemies paths to x, y

	self:assemble_pos(x, y)

	if self.tpr then

		self.thread = love.thread.newThread(self.tpr)
		self.thread:start()

	end


end

function Enemy:patrol( points )

	self.patrol_p = points or self.patrol_p

	-- mostly useless patrol manager, just serves to queue next_patrol up atm

	self:next_patrol()

end

function Enemy:next_patrol()
	if self.patrol_p then
		if self.patrol_p[self.patrol_stage] then

			-- if we have a patrol_p, and patrol_p coords, go there

			self:goto(self.patrol_p[self.patrol_stage].x, self.patrol_p[self.patrol_stage].y)

			self.patrol_stage = self.patrol_stage + 1
		end
	end

end

function Enemy:track_player_t()

	-- find that dastardly player, wherever they may be
	-- (and chase them )

	self:assemble()

	if self.tpr then
		print("tracking player")

		self.thread = love.thread.newThread('tp'..love.timer.getTime(), self.tpr)
		self.thread:start()

	else

		self:assemble()

	end

end

function Enemy:get_paths()

	if self.thread then
		local progress_channel = love.thread.getChannel('path_progress')
		local confirm = progress_channel:pop()

		-- if there is a thread, and it is finished, then...

		if confirm then
			--print("confirmed thread")
			self.path = {}
			self.step = 1
			local pc_channel = love.thread.getChannel("counted_path")
			self.path_tt = pc_channel:pop()
			--print(table.tostring(self.path_tt))
			for i = 1, #self.path_tt do			
				
				local val = self.path_tt[i]
				--print("val : ", val)
				local count, x, y = 0, 0, 0
				val:gsub("^(%d+)-(%d+)-(%d+)$", function(a, b, c) count, x, y = a + 0, b+0, c+0 end)
				self.path[count] = {}
				self.path[count].x = x
				self.path[count].y = y
				
			end
			--print(table.tostring(self.path))
			local len_channel = love.thread.getChannel('len')
			self.path_len =  len_channel:pop()-- length of our path
		else
			-- error code in here
		end

	end

end

function Enemy:update(dt)

	-- er'y body shuffle around

	local min_dist = 10
	local e_upd_spd = 3

	for i = #enemies, 1, -1 do
		if enemies[i] then
			enemies[i]:light() -- checks if the enemy is lit
			enemies[i]:move(dt) -- enemy makes any valid moves
			enemies[i].counter = enemies[i].counter + 1
			if enemies[i].path then -- this is leftover from player tracking...
			-- if the player moves more than min_dist away from where the enemy has
			-- already pathed, the enemy regenerates a path.
			-- if this were enabled, that is
			-- goes as fast as e_upd_spd
				if math.abs(enemies[i].path[#enemies[i].path].x-pX) > min_dist or
					math.abs(enemies[i].path[#enemies[i].path].y-pY) > min_dist then
					if enemies[i].counter > love.timer.getFPS() * e_upd_spd then

						--enemies[i]:track_player_t()
						enemies[i].counter = 0


					end
				end
			end
		end
	end

end

function Enemy:light()

	-- the enemy is lit.. or maybe not

	if player.light == true then
		if p_light_beam_collide(dOX+((self.x-map_x)*(tile_size*scale)),
			dOY+((self.y-map_y)*(tile_size*scale))) then
			self.lit = true
		else
			self.lit = false
		end
	else
		self.lit = false
	end

end

function Enemy:damage( amount )

	self.health = self.health - amount

	if self.health < 0 then
		self:destroy()
	end


end

function Enemy:destroy()

	enemies[self.id] = nil

end

function Enemy:check_collision(x, y)

	local ce = {}

	for i = 1, #enemies do
		if enemies[i] then
			if x >= enemies[i].x and x <= enemies[i].x+tile_size and
				y >= enemies[i].y  and y <= enemies[i].y+tile_size then
				ce[#ce+1] = enemies[i]
			end
		end
	end

	return ce

end

function Enemy:draw()

	-- if they're lit, draw them brightly
	-- if not, draw them dim
	
	if self.lit or SceneCompletelyLit then			
		draw_light(self.image,
			dOX+((self.x-map_x)*(tile_size*scale)),
			dOY+((self.y-map_y)*(tile_size*scale))+self.cadence,
			self.rotation, self.scaleX, self.scaleY)
	else
		draw_dark(self.image,
			dOX+((self.x-map_x)*(tile_size*scale)),
			dOY+((self.y-map_y)*(tile_size*scale))+self.cadence,
			self.rotation, self.scaleX, self.scaleY)			
	end
	

end

function Enemy:move(dt)

	self:get_paths()

	if self.can_move then -- if we are able to move
		if not self.lit then -- and we are not lit (frozen)
			self.counter = self.counter + 1
			if self.counter >= love.timer.getFPS() * self.speed then -- is the counter ready
				self.cadence = -self.cadence -- adds a little wobble on the Y axis when moving,
				-- for a little bobble

				if self.path then -- if we have a path
					if self.path_len > 0 then -- if that path is not to the tile underneath us
						if self.step < #self.path then -- if there are more steps on the path
							--print("step ", self.step, "moving from ",
							--	self.x, " to ", self.path[self.step].x, "and",
							--	self.y, " to ", self.path[self.step].y)
							self.lastX = self.x
							self.lastY = self.y
							self.x = self.path[self.step].x
							self.y = self.path[self.step].y
							self.step = self.step + 1
						elseif self.step == #self.path then -- the final step in the path

							-- things that happen when the enemy reaches it's goal

							--print("step ", self.step, "moving from ",
								--self.x, " to ", self.path[self.step].x, "and",
								--self.y, " to ", self.path[self.step].y)
							self.lastX = self.x
							self.lastY = self.y
							self.x = self.path[self.step].x
							self.y = self.path[self.step].y
							if self.patrol_t and (self.patrol_stage == #self.patrol_p) then self.patrol_stage = 1 end
							self.path = nil
						else
						end
					else
						-- i don't think this area ever gets called, because it becomes
						-- impossible to reach. if shit shows up here, look the fuck out
						self.path = nil

						if self.patrol_t then

							--self.patrol_stage = 1



						end
					end
				else
					--self:track_player_t()
					if self.patrol_t then

						--self.patrol_stage = 1

						self:patrol()

					end
				end
				self.counter = 0
			end
		end
	end

end

function Enemy:track_player()

	-- first attempt at player tracking function, don't think it works, suggest track_player_t or goto

	local path = self.pathf:getPath(self.x, self.y, pX, pY)

	self.path = {}
	self.step = 1

	if path then
		--print(self.name,('Path found! Length: %.2f'):format(path:getLength()))
		self.path_len = path:getLength()
		for node, count in path:nodes() do
			self.path[count] = node
			--print("step :", count, "path x: ", node.x, " path y: ", node.y)
		end
		--print(table.tostring(path))
	end

end
